package com.nlx.notes.support.util;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;

import jakarta.annotation.PostConstruct;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

/**
 * 全局唯一纯数字code生成器（递增版本）
 * 
 * 该工具类用于生成指定长度的全局唯一纯数字code，每次生成的code都比上一次大。
 * 通过结合时间戳和计数器生成基础code，并利用Redis的原子操作确保全局唯一性和递增性。
 * 
 * 使用示例：
 * String code = UniqueCodeGenerator.generateIncrementalUniqueCode(8); // 生成8位递增唯一数字code
 * 
 * 注意事项：
 * 1. code长度必须在6-32位之间
 * 2. 依赖Redis确保全局唯一性，需要确保Redis服务正常运行
 * 3. 生成的code具有30天的缓存时间，防止重复使用
 */
@Slf4j
@Component
public class UniqueCodeGenerator {

    @Autowired(required = false)
    private RedisTemplate<String, Object> redisTemplate;


    private static final int DB_CODE_LENGTH = 16;

    private static final AtomicReference<UniqueCodeGenerator> INSTANCE = new AtomicReference<>();
    private static final AtomicReference<Long> COUNTER = new AtomicReference<>(0L);

    private static final String CODE_PREFIX = "incremental_unique_code:";
    private static final String TIMESTAMP_PATTERN = "yyyyMMddHHmmss";
    private static final String LAST_CODE_KEY = "last_generated_code";
    private static final String CODE_COUNTER_KEY = "code_counter";

    @PostConstruct
    public void init() {
        INSTANCE.set(this);
    }

    public static String  genCode() {
        return generateIncrementalUniqueCode(DB_CODE_LENGTH);
    }


    /**
     * 生成指定长度的全局唯一递增纯数字code
     *
     * @param length code长度(最小6位，最大32位)
     * @return 唯一的递增纯数字code
     */
    public static String generateIncrementalUniqueCode(int length) {
        if (length < 6 || length > 32) {
            throw new IllegalArgumentException("code长度必须在6-32位之间");
        }

        // 获取实例
        UniqueCodeGenerator instance = INSTANCE.get();
        if (instance != null) {
            // 使用Redis确保全局唯一性和递增性
            return instance.getIncrementalUniqueCode(length);
        } else {
            // 如果实例不存在，回退到基础实现
            return generateFallbackCode(length);
        }
    }

    /**
     * 通过Redis确保code的全局唯一性和递增性
     *
     * @param length code长度
     * @return 唯一的递增code
     */
    @SuppressWarnings("unchecked")
    private String getIncrementalUniqueCode(int length) {
        // 如果Redis不可用，则回退到基础实现
        if (redisTemplate == null) {
            log.warn("RedisTemplate不可用，回退到基础实现");
            return generateFallbackCode(length);
        }

        ValueOperations<String, Object> ops = redisTemplate.opsForValue();

        // 重试机制，最多重试10次
        int retryCount = 0;
        while (retryCount < 10) {
            try {
                // 使用Redis的原子操作生成递增的唯一code
                // 1. 获取当前时间戳
                String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern(TIMESTAMP_PATTERN));

                // 2. 使用Redis的incr命令原子性地增加计数器
                Long counter = ops.increment(CODE_COUNTER_KEY, 1);

                // 3. 构造code: 时间戳 + 计数器
                String newCode = timestamp + String.format("%04d", counter % 10000);

                // 4. 确保新code长度正确
                if (newCode.length() < length) {
                    // 如果长度不足，补充随机数
                    StringBuilder codeBuilder = new StringBuilder(newCode);
                    while (codeBuilder.length() < length) {
                        codeBuilder.append(ThreadLocalRandom.current().nextInt(0, 10));
                    }
                    newCode = codeBuilder.toString();
                } else if (newCode.length() > length) {
                    // 如果长度超出，截取末尾部分
                    newCode = newCode.substring(newCode.length() - length);
                }

                // 5. 使用Redis的原子操作确保唯一性
                String key = CODE_PREFIX + newCode;
                Boolean success = ops.setIfAbsent(key, "1", 30, TimeUnit.DAYS);

                if (success != null && success) {
                    // 更新最后生成的code
                    ops.set(LAST_CODE_KEY, newCode, 30, TimeUnit.DAYS);
                    log.debug("成功生成递增唯一code: {}", newCode);
                    return newCode;
                } else {
                    // 如果code已被占用，继续重试
                    log.debug("code {} 已被占用，继续重试", newCode);
                }
            } catch (Exception e) {
                log.warn("Redis操作失败，回退到基础实现", e);
                return generateFallbackCode(length);
            }

            retryCount++;
            // 短暂等待后重试
            try {
                Thread.sleep(1 + ThreadLocalRandom.current().nextInt(10));
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                break;
            }
        }

        // 如果重试10次仍未获得唯一code，则回退到基础实现
        log.error("无法生成唯一的递增code，已重试10次，回退到基础实现");
        return generateFallbackCode(length);
    }

    /**
     * 回退实现（当Redis不可用时使用）
     *
     * @param length code长度
     * @return 生成的code
     */
    private static String generateFallbackCode(int length) {
        // 使用计数器和纳秒生成相对唯一的code
        long counter = COUNTER.updateAndGet(v -> v + 1);
        String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern(TIMESTAMP_PATTERN));
        
        StringBuilder codeBuilder = new StringBuilder();
        if (timestamp.length() >= length) {
            codeBuilder.append(timestamp.substring(0, length));
        } else {
            codeBuilder.append(timestamp);
            // 添加计数器和随机数确保唯一性
            String uniquePart = String.valueOf(counter % 1000000) + String.valueOf(System.nanoTime() % 1000);
            int remainingLength = length - codeBuilder.length();
            if (uniquePart.length() <= remainingLength) {
                codeBuilder.append(uniquePart);
            } else {
                codeBuilder.append(uniquePart.substring(0, remainingLength));
            }
            
            // 如果仍然不够长度，用随机数补齐
            while (codeBuilder.length() < length) {
                codeBuilder.append(ThreadLocalRandom.current().nextInt(0, 10));
            }
        }
        
        return codeBuilder.toString();
    }
    
    // 保留原有的generateUniqueCode方法以保持向后兼容性
    /**
     * 生成指定长度的全局唯一纯数字code（原有方法，保持向后兼容）
     *
     * @param length code长度(最小6位，最大32位)
     * @return 唯一的纯数字code
     */
    public static String generateUniqueCode(int length) {
        if (length < 6 || length > 32) {
            throw new IllegalArgumentException("code长度必须在6-32位之间");
        }

        // 生成基础code(时间戳+随机数)
        String baseCode = generateBaseCode(length);

        // 获取实例
        UniqueCodeGenerator instance = INSTANCE.get();
        if (instance != null) {
            // 使用Redis确保全局唯一性
            return instance.getUniqueCode(baseCode, length);
        } else {
            // 如果实例不存在，直接返回基础code
            // 为了增加唯一性，添加计数器和纳秒部分
            long counter = COUNTER.updateAndGet(v -> v + 1);
            String uniquePart = String.valueOf(System.nanoTime() % 1000000) + String.valueOf(counter % 1000);
            if (baseCode.length() + uniquePart.length() <= length) {
                return baseCode + uniquePart;
            } else {
                // 如果超长，则截取
                String combined = baseCode + uniquePart;
                return combined.substring(combined.length() - length);
            }
        }
    }

    /**
     * 生成基础code(时间戳+随机数) - 原有方法保持不变
     *
     * @param length code长度
     * @return 基础code
     */
    private static String generateBaseCode(int length) {
        // 获取当前时间戳
        String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern(TIMESTAMP_PATTERN));
        
        // 计算剩余需要填充的随机数位数
        int remainingLength = length - Math.min(timestamp.length(), length);
        
        StringBuilder codeBuilder = new StringBuilder();
        
        // 添加时间戳(如果长度足够)
        if (timestamp.length() <= length) {
            codeBuilder.append(timestamp);
        } else {
            // 如果时间戳长度超过所需长度，则截取前面部分
            codeBuilder.append(timestamp.substring(0, length));
            return codeBuilder.toString();
        }
        
        // 填充随机数
        if (remainingLength > 0) {
            String randomNum = generateRandomNumber(remainingLength);
            codeBuilder.append(randomNum);
        }
        
        return codeBuilder.toString();
    }

    /**
     * 生成指定位数的随机数字 - 原有方法保持不变
     *
     * @param length 随机数位数
     * @return 随机数字字符串
     */
    private static String generateRandomNumber(int length) {
        StringBuilder sb = new StringBuilder();
        // 第一位不能为0
        sb.append(ThreadLocalRandom.current().nextInt(1, 10));
        // 后续位可以为0-9任意数字
        for (int i = 1; i < length; i++) {
            sb.append(ThreadLocalRandom.current().nextInt(0, 10));
        }
        return sb.toString();
    }

    /**
     * 通过Redis确保code的全局唯一性 - 原有方法保持不变
     *
     * @param baseCode 基础code
     * @param length   code长度
     * @return 唯一的code
     */
    @SuppressWarnings("unchecked")
    private String getUniqueCode(String baseCode, int length) {
        // 如果Redis不可用，则直接返回基础code
        if (redisTemplate == null) {
            log.warn("RedisTemplate不可用，直接返回基础code: {}", baseCode);
            return baseCode;
        }
        
        ValueOperations<String, Object> ops = (ValueOperations<String, Object>) redisTemplate.opsForValue();
        String key = CODE_PREFIX + baseCode;
        
        // 尝试设置key，如果设置成功说明该code未被使用
        Boolean success;
        try {
            success = ops.setIfAbsent(key, "1", 30, TimeUnit.DAYS);
            log.debug("尝试设置key: {}, 结果: {}", key, success);
        } catch (Exception e) {
            log.warn("Redis操作失败，直接返回基础code: {}", baseCode, e);
            return baseCode;
        }
        
        if (success != null && success) {
            log.debug("成功生成唯一code: {}", baseCode);
            return baseCode;
        }
        
        // 如果code已被占用，则重新生成直到获得唯一的code
        int retryCount = 0;
        while (retryCount < 10) { // 最多重试10次
            String newCode = generateBaseCode(length);
            key = CODE_PREFIX + newCode;
            
            // 检查key是否存在
            Boolean exists;
            try {
                exists = redisTemplate.hasKey(key);
                log.debug("检查key是否存在: {}, 结果: {}", key, exists);
            } catch (Exception e) {
                log.warn("Redis检查key是否存在失败，直接返回code: {}", newCode, e);
                return newCode;
            }
            
            if (exists != null && exists) {
                log.debug("key已存在，继续重试: {}", key);
                retryCount++;
                continue;
            }
            
            try {
                success = ops.setIfAbsent(key, "1", 30, TimeUnit.DAYS);
                log.debug("尝试设置key: {}, 结果: {}", key, success);
            } catch (Exception e) {
                log.warn("Redis设置key失败，直接返回code: {}", newCode, e);
                return newCode;
            }
            
            if (success != null && success) {
                log.debug("成功生成唯一code: {}", newCode);
                return newCode;
            }
            retryCount++;
        }
        
        // 如果重试10次仍未获得唯一code，则抛出异常
        log.error("无法生成唯一的code，已重试10次");
        throw new RuntimeException("无法生成唯一的code，请稍后重试");
    }
}